
#include "nane/core/WinAPI/winapi.h"

#include <cassert>
#include <vector>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool WinAPI::SupportUnicode()
    {
        static bool s_initialized = false;
        static bool s_supportUnicode = false;
        if( s_initialized == true )
        {
            return s_supportUnicode;
        }
        // else
        HMODULE hKernel32 = ::LoadLibraryW( L"Kernel32.dll" );
        if( hKernel32 != NULL )
        {
            ::FreeLibrary( hKernel32 );
            s_supportUnicode = true;
        }

        s_initialized = true;
        return s_supportUnicode;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::Utf8ToWstr( const char* _utf8, wchar_t* _wstr, uint32 _wstrSize )
    {
        ::MultiByteToWideChar(CP_UTF8, 0, _utf8, -1, _wstr, static_cast<int>(_wstrSize));
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::Utf8ToWstr( const std::string& _utf8, std::wstring* _wstr )
    {
        assert( _wstr != NULL );
        if( _wstr != NULL )
        {
            const char* cutf8 = _utf8.c_str();
            int size = ::MultiByteToWideChar( CP_UTF8, 0, cutf8, -1, 0, 0 );
            std::vector<wchar_t> buffer( size );
            ::MultiByteToWideChar( CP_UTF8, 0, cutf8, -1, &(buffer[0]), size );
            _wstr->assign( &(buffer[0]) );
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::WstrToUtf8( const std::wstring& _wstr, std::string* _utf8 )
    {
        assert( _utf8 != NULL );
        if( _utf8 != NULL )
        {
            const wchar_t* cwstr = _wstr.c_str();
            int size = ::WideCharToMultiByte( CP_UTF8, 0, cwstr, -1, 0, 0, 0, 0 );
            std::vector<char> buffer(size);
            ::WideCharToMultiByte( CP_UTF8, 0, cwstr, -1, &(buffer[0]), size, NULL, NULL );
            _utf8->assign( &(buffer[0]) );
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::AnsiToWstr( const std::string& _ansi, std::wstring* _wstr )
    {
        assert( _wstr != NULL );
        if( _wstr != NULL )
        {
            const char* cansi = _ansi.c_str();
            int size = ::MultiByteToWideChar( CP_ACP, 0, cansi, -1, 0, 0 );
            std::vector<wchar_t> buffer( size );
            ::MultiByteToWideChar( CP_ACP, 0, cansi, -1, &(buffer[0]), size );
            _wstr->assign( &(buffer[0]) );
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::WstrToAnsi( const wchar_t* _wstr, char* _ansi, uint32 _ansiSize )
    {
        ::WideCharToMultiByte( CP_ACP, 0, _wstr, -1, _ansi, _ansiSize, NULL, NULL );
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::WstrToAnsi( const std::wstring& _wstr, std::string* _ansi )
    {
        assert( _ansi != NULL );
        if( _ansi != NULL )
        {
            const wchar_t* cwstr = _wstr.c_str();
            int size = ::WideCharToMultiByte( CP_ACP, 0, cwstr, -1, 0, 0, 0, 0 );
            std::vector<char> buffer(size);
            ::WideCharToMultiByte( CP_ACP, 0, cwstr, -1, &(buffer[0]), size, NULL, NULL );
            _ansi->assign( &(buffer[0]) );
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::Utf8ToAnsi( const char* _utf8, char* _ansi, uint32 _ansiSize )
    {
        std::wstring wstr;
        Utf8ToWstr(_utf8, &wstr);
        WstrToAnsi(wstr.c_str(), _ansi, _ansiSize);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::Utf8ToAnsi( const std::string& _utf8, std::string* _ansi )
    {
        std::wstring wstr;
        Utf8ToWstr( _utf8, &wstr );
        WstrToAnsi( wstr, _ansi );
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::AnsiToUtf8( const std::string& _ansi, std::string* _utf8 )
    {
        std::wstring wstr;
        AnsiToWstr( _ansi, &wstr );
        WstrToUtf8( wstr, _utf8 );
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HANDLE WinAPI::CreateFileA( const std::string& _lpFileName, DWORD _dwDesiredAccess, DWORD _dwShareMode,
                                LPSECURITY_ATTRIBUTES _lpSecurityAttributes, DWORD _dwCreationDisposition,
                                DWORD _dwFlagsAndAttributes, HANDLE _hTemplateFile )
    {
        std::string filenameA;
        Utf8ToAnsi(_lpFileName, &filenameA);
        return ::CreateFileA(filenameA.c_str(), _dwDesiredAccess, _dwShareMode,
                                _lpSecurityAttributes, _dwCreationDisposition,
                                _dwFlagsAndAttributes, _hTemplateFile);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HANDLE WinAPI::CreateFileW( const std::string& _lpFileName, DWORD _dwDesiredAccess, DWORD _dwShareMode,
                                LPSECURITY_ATTRIBUTES _lpSecurityAttributes, DWORD _dwCreationDisposition,
                                DWORD _dwFlagsAndAttributes, HANDLE _hTemplateFile )
    {
        std::wstring filenameW;
        Utf8ToWstr(_lpFileName, &filenameW);

        return ::CreateFileW(filenameW.c_str(), _dwDesiredAccess, _dwShareMode,
                                _lpSecurityAttributes, _dwCreationDisposition,
                                _dwFlagsAndAttributes, _hTemplateFile);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    DWORD WinAPI::GetFileAttributesA( const std::string& _lpFileName )
    {
        std::string filenameA;
        Utf8ToAnsi(_lpFileName, &filenameA);

        return ::GetFileAttributesA(filenameA.c_str());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    DWORD WinAPI::GetFileAttributesW( const std::string& _lpFileName )
    {
        std::wstring filenameW;
        Utf8ToWstr(_lpFileName, &filenameW);

        return ::GetFileAttributesW(filenameW.c_str());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::DeleteFileA( const std::string& _lpFileName )
    {
        std::string filenameA;
        Utf8ToAnsi(_lpFileName, &filenameA);

        return ::DeleteFileA(filenameA.c_str());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::DeleteFileW( const std::string& _lpFileName )
    {
        std::wstring filenameW;
        Utf8ToWstr(_lpFileName, &filenameW);

        return ::DeleteFileW(filenameW.c_str());
    }
    //////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::CreateDirectoryA( const std::string& _lpDirectoryPath, LPSECURITY_ATTRIBUTES _lpSecurityAttributes )
    {
        std::string directoryPathA;
        Utf8ToAnsi(_lpDirectoryPath, &directoryPathA);

        return ::CreateDirectoryA(directoryPathA.c_str(), _lpSecurityAttributes);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::CreateDirectoryW( const std::string& _lpDirectoryPath, LPSECURITY_ATTRIBUTES _lpSecurityAttributes )
    {
        std::wstring directoryPathW;
        Utf8ToWstr(_lpDirectoryPath, &directoryPathW);

        return ::CreateDirectoryW(directoryPathW.c_str(), _lpSecurityAttributes);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::RemoveDirectoryA( const std::string& _lpDirectoryPath )
    {
        std::string directoryPathA;
        Utf8ToAnsi(_lpDirectoryPath, &directoryPathA);

        return ::RemoveDirectoryA(directoryPathA.c_str());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::RemoveDirectoryW( const std::string& _lpDirectoryPath )
    {
        std::wstring directoryPathW;
        Utf8ToWstr(_lpDirectoryPath, &directoryPathW);

        return ::RemoveDirectoryW(directoryPathW.c_str());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HWND WinAPI::CreateWindowA( const char* _lpClassName, const char* _lpWindowName, DWORD _dwStyle
                          , int _x, int _y, int _nWidth, int _nHeight, HWND _hWndParent
                          , HMENU _hMenu, HINSTANCE _hInstance, LPVOID _lpParam )
    {
        const char* lpClassNameA = NULL;
        std::string lpClassName;
        if ( _lpClassName != NULL )
        {
            Utf8ToAnsi(_lpClassName, &lpClassName);
            lpClassNameA = lpClassName.c_str();
        }
        const char* lpWindowNameA = NULL;
        std::string lpWindowName;
        if ( _lpWindowName != NULL )
        {
            Utf8ToAnsi(_lpWindowName, &lpWindowName);
            lpWindowNameA = lpWindowName.c_str();
        }
        return ::CreateWindowExA(0, lpClassNameA, lpWindowNameA, _dwStyle, _x, _y, _nWidth, _nHeight, _hWndParent, _hMenu, _hInstance, _lpParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HWND WinAPI::CreateWindowW( const char* _lpClassName, const char* _lpWindowName, DWORD _dwStyle
                              , int _x, int _y, int _nWidth, int _nHeight, HWND _hWndParent
                              , HMENU _hMenu, HINSTANCE _hInstance, LPVOID _lpParam )
	{
        const wchar_t* lpClassNameW = NULL;
        std::wstring lpClassName;
        if( _lpClassName != NULL )
        {
            Utf8ToWstr(_lpClassName, &lpClassName);
            lpClassNameW = lpClassName.c_str();
        }
        const wchar_t* lpWindowNameW = NULL;
        std::wstring lpWindowName;
        if( _lpWindowName != NULL )
        {
            Utf8ToWstr(_lpWindowName, &lpWindowName);
            lpWindowNameW = lpWindowName.c_str();
        }
        return ::CreateWindowExW(0, lpClassNameW, lpWindowNameW, _dwStyle, _x, _y, _nWidth, _nHeight, _hWndParent, _hMenu, _hInstance, _lpParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::GetCommandLineA( std::string* _commandLine )
    {
        assert( _commandLine != NULL );
        char* cmdLineA = ::GetCommandLineA();
        AnsiToUtf8(cmdLineA, _commandLine);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void WinAPI::GetCommandLineW( std::string* _commandLine )
    {
        assert( _commandLine != NULL );
        char* cmdLineA = ::GetCommandLineA();
        AnsiToUtf8(cmdLineA, _commandLine);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ATOM WinAPI::RegisterClassA( const char* _className, HINSTANCE _hInstance, WNDPROC _wndProc )
    {
        char classNameA[256];
        Utf8ToAnsi(_className, classNameA, 256);
        WNDCLASSA wndClassA;
        wndClassA.style = 0;
        wndClassA.lpfnWndProc = _wndProc;
        wndClassA.cbClsExtra = 0;
        wndClassA.cbWndExtra = 0;
        wndClassA.hInstance = _hInstance;
        wndClassA.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
        wndClassA.hCursor = ::LoadCursor(NULL, IDC_ARROW);
        wndClassA.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
        wndClassA.lpszMenuName = NULL;
        wndClassA.lpszClassName = classNameA;
        return ::RegisterClassA(&wndClassA);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    ATOM WinAPI::RegisterClassW( const char* _className, HINSTANCE _hInstance, WNDPROC _wndProc )
    {
        wchar_t classNameW[256];
        Utf8ToWstr(_className, classNameW, 256);
        WNDCLASSW wndClassW;
        wndClassW.style = 0;
        wndClassW.lpfnWndProc = _wndProc;
        wndClassW.cbClsExtra = 0;
        wndClassW.cbWndExtra = 0;
        wndClassW.hInstance = _hInstance;
        wndClassW.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);;
        wndClassW.hCursor = ::LoadCursor(NULL, IDC_ARROW);
        wndClassW.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
        wndClassW.lpszMenuName = NULL;
        wndClassW.lpszClassName = classNameW;
        return ::RegisterClassW(&wndClassW);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::UnregisterClass( ATOM _classAtom, HINSTANCE _hInstance )
    {
        DWORD classAtom = MAKELONG(_classAtom, 0);
        return ::UnregisterClassA(reinterpret_cast<const char*>(classAtom), _hInstance);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HWND WinAPI::CreateWindowA( ATOM _classAtom, const char* _windowName, DWORD _style
                                                      , int _x, int _y, int _width, int _height, HWND _hWndParent
                                                      , HMENU _hMenu, HINSTANCE _hInstance, void* _param )
    {
        DWORD classAtom = MAKELONG(_classAtom, 0);
        std::string windowNameA;
        Utf8ToAnsi(_windowName, &windowNameA);
        return ::CreateWindowExA(0, reinterpret_cast<const char*>(classAtom), windowNameA.c_str(), _style, _x, _y, _width, _height, _hWndParent, _hMenu, _hInstance, _param);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    HWND WinAPI::CreateWindowW( ATOM _classAtom, const char* _windowName, DWORD _style
                                                      , int _x, int _y, int _width, int _height, HWND _hWndParent
                                                      , HMENU _hMenu, HINSTANCE _hInstance, void* _param )
    {
        DWORD classAtom = MAKELONG(_classAtom, 0);
        std::wstring windowNameW;
        Utf8ToWstr(_windowName, &windowNameW);
        return ::CreateWindowExW(0, reinterpret_cast<const wchar_t*>(classAtom), windowNameW.c_str(), _style, _x, _y, _width, _height, _hWndParent, _hMenu, _hInstance, _param);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::PeekMessageA( LPMSG _msg, HWND _hWnd, UINT _msgFilterMin, UINT _msgFilterMax, UINT _removeMsg )
    {
        return ::PeekMessageA(_msg, _hWnd, _msgFilterMin, _msgFilterMax, _removeMsg);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL WinAPI::PeekMessageW( LPMSG _msg, HWND _hWnd, UINT _msgFilterMin, UINT _msgFilterMax, UINT _removeMsg )
    {
        return ::PeekMessageW(_msg, _hWnd, _msgFilterMin, _msgFilterMax, _removeMsg);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::DispatchMessageA( const MSG *_msg )
    {
        return ::DispatchMessageA(_msg);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::DispatchMessageW( const MSG *_msg )
    {
        return ::DispatchMessageW(_msg);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::DefWindowProcA( HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        return ::DefWindowProcA(_hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::DefWindowProcW( HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        return ::DefWindowProcW(_hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LONG_PTR WinAPI::SetWindowLongPtrA( HWND _hWnd, int _index, LONG_PTR _newLong )
    {
        return ::SetWindowLongPtrA(_hWnd, _index, _newLong);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LONG_PTR WinAPI::SetWindowLongPtrW( HWND _hWnd, int _index, LONG_PTR _newLong )
    {
        return ::SetWindowLongPtrW(_hWnd, _index, _newLong);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LONG_PTR WinAPI::GetWindowLongPtrA( HWND _hWnd, int _index )
    {
        return ::GetWindowLongPtrA(_hWnd, _index);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LONG_PTR WinAPI::GetWindowLongPtrW( HWND _hWnd, int _index )
    {
        return ::GetWindowLongPtrW(_hWnd, _index);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::CallWindowProcA( WNDPROC _wndProc, HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        return ::CallWindowProcA(_wndProc, _hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT WinAPI::CallWindowProcW( WNDPROC _wndProc, HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        return ::CallWindowProcW(_wndProc, _hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
